;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; INTEGRATION PLAN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

#|
 1. Use Basic Icarus system including FastMatcher inference
 2. Separate primitive concepts from hierarchical (macro) concepts
       (concepts can have either :PERCEPTS or :RELATIONS but NOT BOTH)
 3. Hierarchical concepts are added as inference rules to the Abductive WIE (Will's Inference Engine)
 4. Use primitive concepts for perception - resulting cstm* copied to observations*
 5. Observations are fed to WIE as observations
 6. Run WIE some number of inference steps
 7. Extract inferred beliefs -> wie-beliefs* 
 8. Combine observations* with wie-beliefs* --> cstm*  (delete wie-beliefs that are negated literals)
 9. Run Icarus problem-solver / execution using combined cstm*
10. (if necessary) reset cstm* back to observations* (pure results of last FM inference steps)

QUESTIONs:
  Can the WIE be reused each cycle? Or need to create a new one?
  Will converting hierarchical concepts into WIE rules work for recursive concepts?
      ie does WIE support recursive rules?

Procedures:
 SETUP:
   Separate concepts into primitive and hierarchical
   [If WIE can be reused:]
      Create a WIE
      Load it (1-time)  with hierarchical concepts as rules

 CYCLE:
  Get Perceptions
  Make inferences (Fast Matcher) using primitive concepts
  cstm* -> observations*
  [If WIE can be reused:]
     Reset WIE (clear inferences and observations)
  [IF WIE cannot be reused:]
     Create new WIE
     Load hierarchical-concepts as rules
  Load observations into WIE
  Run WIE inference for some number of steps
  Extract new inferences
  New inferences + observations -> cstm*
  Run SOLVE (problem solver / execution) as usual using this cstm*
  Reset cstm* to only the observations (to prepare for next perctual / FM infer cycle)
|#

(setf inference* 'wie)

     
(defparameter cltm-original* nil) ; all concepts
(defparameter cltm-primitive* nil)
(defparameter cltm-higher-level* nil)

(defparameter cstm-save* nil)
(defparameter cstm-integration* nil)  ; 

(defparameter wie* nil)			; global to hold wie object
(defparameter reuse-wie?* nil)		; switch to determine whether to re-init or reuse wie on each cycle
(defparameter observations* nil)
(defparameter rules* nil)

(defparameter count-inferences?* t)
(defparameter inference-count-data* nil)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SETUP - call once in RUN ? (or when extract-unchainables is called)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun integration-setup ()
  (setf cltm-primitive* nil
	cltm-higher-level* nil
	cltm-original* cltm*)
  (loop for concept in cltm*
       do
       (cond ((null (concept-relations concept))
	      (push concept cltm-primitive*))
	     (t 
	      (push concept cltm-higher-level*))))
  (setf cltm* cltm-primitive*)
  (setf rules* (convert-high-level-concepts-to-rules cltm-higher-level*))
  (setf inference-count-data* nil)
  ; create a wie ?
  )

(defun convert-high-level-concepts-to-rules (high-level-concepts)
  (loop for concept in high-level-concepts
       collect
       (convert-high-level-concept-to-rule concept)))

(defun convert-high-level-concept-to-rule (concept)
  ; rule format: 
  (list '=>
	(cons 'and (concept-relations concept))
	(concept-head concept)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; MODIFIED PRINT-ICARUS-SETTINGS  (overlay definition in holdover.lisp)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun print-icarus-settings ()
  (terpri)(princ "========= Current Icarus Setting =========")
  (terpri)(princ " Inference Engine: ")
  (print-setting inference*
		 '(standard "Standard")
		 '(fast "Fast Matcher")
		 '(wie "Wie with Standard"))
  (terpri)(princ " Skill Selection Heuristic: ")
  (print-setting skill-selection-heuristic* 
		 '(:MAX-EFFECTS-MATCHED "Maximize Matched Effects")
		 '(:MIN-UNSATISFIED-CONDITIONS "Minimize Unsatisfied Conditions"))
  (terpri)(princ "=========================================="))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; MODIFIED RUN-AUX
;;;    overlay or replace old RUN-AUX ?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


(defun run-aux (n
		&key (timelimit 0) (mode nil))
  (check-essential-functions)

  ;; Compile a list of unchainable conditions for each skill
  (when (< cycle* 2)
    (extract-unchainable-conditions cltm* sltm*) ; do using all concepts (before separating primitives)
    ;; do integration setup
    (if (equal inference* 'WIE)
	(integration-setup)))

  (do ()
      ((or (= cycle* n)
	   (and (> timelimit 0)
		(>= time* timelimit))
	   halt*)
       (cond ((and (> timelimit 0)
		   (>= time* timelimit))
	      (print-timeout))
	     ((equal halt* 'noskill)
	      (print-no-skill))
	     ((equal halt* 'success)
	      (print-achieved))))
      (setq current-resources* nil)
      (setq learned-paths-used* nil)
      (sleep 0.1)
      (let ((starttime (get-internal-real-time))
	    percepts
	    agent-level-results)
	(setq pstm* (preattend))
	(setq percepts (copy-list pstm*))
	(cond ((equal inference* 'STANDARD) 
	       ;; always set cstm* to NIL (otherwise beliefs don't get deleted when no longer true)
	       (setf cstm* nil)
	       ;(unless (boundp 'cstm*)
	       ;   (setf cstm* nil))
	       (setq cstm* (infer cltm* pstm* cstm*)))
	      ((equal inference* 'FAST)
	       (run-inference))
	      ((equal inference* 'WIE)
	       (run-inference-wie))
	      (t
	       (error "The parameter INFERENCE* is not set up properly!")))
	(setq astm* nil)
	;; update the belief heads (literals)
	(setf cstmh*
	      (mapcar #'cinstance-head cstm*))
      ;Store the initial state
	(cond ((= cycle* 1)
	       (push pstm* pstatetrace*)
	       (push cstm* cstatetrace*)))
      ;If some action was taken in the previous cycle,
      ;store the modified state.
	;(cond ((not (null last-execution*))
	       ;(push pstm* pstatetrace*)
	       ;(push cstm* cstatetrace*)))
	;(setq last-execution* nil)
	(print-memory-traces)
	;;; REWRITE CODE FOLLOWS:
	(select-active-problem)
	(solve active-problem* executing-intention*)
	;; Reset Memory to primitive for next cycle
	(if (equal inference* 'WIE)
	    (restore-primitive-memory))
	)
    (update-world)
    (when pprint-state*
      (pprint-state))
    (setq cycle* (1+ cycle*))))


;; set concepts and beliefs to primitive versions
;;    primitive concepts
;;    beliefs from run-inference with only primitive-concepts
(defun restore-primitive-memory ()
  (setf cstm* cstm-save*
	cltm* cltm-primitive*))

(defun save-primitive-memory ()
  (setf cstm-save* cstm*))


;; Run-inference using Will's Inference Engine
;;   Assumed to start with primitive concepts
(defun run-inference-wie ()
  ;; Pat doesn't want Fast-matcher, so use older INFER (but it doesn't work!!)
  (setf cstm* nil)
  (setq cstm* (infer cltm* pstm* cstm*))
  ;; run-inference (regular fast-matcher) with primitive-concepts, percepts
  ;;     [Pat doesn't want fast-matcher (so try to use STANDARD for primitive beliefs)]
  ;;   (run-inference)
  ;; save these primitive-beliefs
  (save-primitive-memory)
  (unless reuse-wie?*
    ;; create a new WIE
    (setf wie* (create-empty-wie))
    ;; prepare rules
    (add-rules-to-wie rules* wie*))
  ;; prepare-observations
  (setf observations* (collect-observations cstm*))
  ;; feed observations to WIE
  (add-observations-to-wie observations* wie*)
  ;; run WIE inference
  (run-wie-inference wie*)			; specify number of steps
  ;; extract inferences
  (setf wie-beliefs*			; in form of literals
	(extract-wie-beliefs wie*))
  ;; add wie beliefs to cstm*
  (setf cstm* (append cstm*
		      (convert-belief-literals-to-cinstances wie-beliefs*))) ; use cstm-original*
  ;; restore original (complete) cltm* for problem-solving
  (setf cltm* cltm-original*)
  )

	
;; original state
    ; cltm*  = cltm-original*
    ; cstm*  = nil

;; primitive-state
   ; cltm* = cltm-primitive*
   ; (then do run-inference)
   ; cstm* = primitive-beliefs   NEED TO SAVE THESE !!

;; state-for-problem-solving (after running wie)
   ; cltm* = cltm-original*
   ; cstm* = primitve-beliefs + wie-beliefs


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; INTERFACE TO WILL'S INFERENCE ENGINE (WIE)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; NOTE: either need to put will's code in special package, or select better global names

;; assume list format for wie* ( wm kb )
(defun create-empty-wie ()
  ;; create a new wie
  ;;   inits global vars wm and kb  (need better global names)
  (initialize)  ; defined in init.lisp  (problably need a new name)
  (add-sensor 'icarus wm)
  (list wm kb)				; for value of wie* - provide access to wm and kb
  )

(defun add-rules-to-wie (rules wie)
  (loop with (wm kb) = wie
     for rule in rules
     do
     ;; add rule to wie
     (store-rule rule kb)
     ))

(defun collect-observations (cstm)
  (loop for cinstance in cstm
       collect
       (cinstance-head cinstance)))

(defun add-observations-to-wie (observations wie)
  (let ((wm (first wie)))
    (add-facts observations 'icarus wm)))

(defun run-wie-inference (wie &optional (step-count 1000))
  (loop with (wm kb) = wie
     with inference-counts = nil
     repeat step-count
     do
     (infer-wie wm kb :bc-only? nil
		:focused? nil :p -1
		:pick-belief #'pick-belief-random)
     (when count-inferences?*
       (push (length (get-local-beliefs (wm-prime wm)))
	     inference-counts))
     finally
       (push inference-counts inference-count-data*)))

;; return inferences / conclusions as a list of literals
(defun extract-wie-beliefs (wie)
  (loop with (wm kb) = wie
       for assumption in (get-local-beliefs (wm-prime wm))
       collect
       (belief-content (second assumption))       ; this should be just the literal
       ))

;; Note: this ignores negated belief-literals
(defun convert-belief-literals-to-cinstances (belief-literals)
  (loop for literal in belief-literals
       for concept = (unless (eql 'not (first literal))
		       (find-concept-match-for-positive-literal literal
								cltm-original*))  ;; use cltm-original*
       for cinstance = (let ((cinst (make-cinstance :head literal)))
			 (when concept
			   (setf (cinstance-id cinst) (concept-id concept)))
			 cinst)
       collect
       cinstance))

;; returns first match.  Maybe could find all matches and random-choose ?
(defun find-concept-match-for-positive-literal (pos-literal cltm)
  (loop for concept in cltm
	  ;; Note: Here the order of arguments to unify-match matters.
	  for (flag . bindings) = (unify-match pos-literal
					       (concept-head concept))
     when flag
     return concept))
